-module(queuemanager).
-export([start/3, loop/5, writeToLog/2, loescheVeralteteUngueltigeNachrichtenAusHBQ/2,
			 fehlerNachrichtInDLQEinfuegen/3, gebeMinElemFromDLQ/1, istDLQLimitUeberschritten/2,
			 verschiebeVonHBQInDLQ/4, organisiereDropMessage/5, istHBQLimitUeberschritten/2,
			 gebeNaechsteNachricht/2, gebeNaechsteNachrichtR/2, gebeMaxNr/1,gebeMaxNrR/2, gebeMinNr/1,sendeNachrichtenAusDLQBisMax/5,
			 gebeMinNrFromDLQ/1, gebeNachrichtMitNr/2, fuegeNachrichtInHBQ/3, fuegeHintenAn/2,istNachrichtVerschiebenMoeglich/2, organisiereGetMessages/4]).

%queuemanager:start(Servername, DlqLimit,self())
start(Servername, DLQLimit, ServerPID) -> 	HBQ = [],
								DLQ = [],
								loop(HBQ, DLQ, DLQLimit, Servername, ServerPID).

				
loop(HBQ, DLQ, DLQLimit, Servername, ServerPID)	->
			%get new message from server
			receive		{dropmessage, {Nachricht, Number}} -> 
										{NewHBQ, NewDLQ} = organisiereDropMessage({Nachricht, Number},HBQ, DLQ, DLQLimit, Servername),								
										loop(NewHBQ, NewDLQ, DLQLimit, Servername, ServerPID);
						{getmessages,PID,Nummer} ->  	organisiereGetMessages(PID, Nummer, ServerPID, DLQ),
										loop(HBQ, DLQ, DLQLimit, Servername, ServerPID);
																				
						feierAbend -> werkzeug:logstop(),
							      true
						%Any ->
						%	Any,
						%	loop(HBQ, DLQ, DLQLimit, Servername, ServerPID)
			end.
	

organisiereGetMessages(PID, Nummer, ServerPID, DLQ) -> 
												MaxInDLQ = gebeMaxNr(DLQ),
												if
													
													Nummer > MaxInDLQ -> ServerPID ! {queueman, PID,{ 0, "keine neuen Nachrichten", true}};
													
													Nummer =< MaxInDLQ  -> sendeNachrichtenAusDLQBisMax(PID, Nummer, MaxInDLQ, ServerPID, DLQ)
												
												end.
												
sendeNachrichtenAusDLQBisMax(PID, StartNummer, MaxInDLQ, ServerPID, DLQ) ->
												MinNr = gebeMinNr(DLQ),
												if
													MinNr =< StartNummer 	-> GesuchteNummer = StartNummer;
													MinNr > StartNummer 	-> GesuchteNummer = MinNr
												end,
												
												{Nachricht, Nummer} = gebeNachrichtMitNr(GesuchteNummer, DLQ),
												
												if 
													Nummer /= 0 andalso GesuchteNummer == MaxInDLQ -> ServerPID ! {queueman,PID, { Nummer, Nachricht , true}};
													GesuchteNummer < MaxInDLQ   			-> ServerPID ! {queueman,PID, { Nummer, Nachricht , false}}
																	%sendeNachrichtenAusDLQBisMax(PID, GesuchteNummer+1, MaxInDLQ, ServerPID, DLQ)
												end.
												
												
	
organisiereDropMessage({Nachricht, Number},HBQ, DLQ, DLQLimit, Servername) ->
								%first step - put in message
								{NewHBQ} 		= fuegeNachrichtInHBQ({Nachricht, Number}, HBQ, Servername),
								
								%loescheVeralteteUngueltigeNachrichtenAusHBQ
								{HBQ2, DLQ2} = loescheVeralteteUngueltigeNachrichtenAusHBQ(NewHBQ, DLQ),
								
								%look if now messages can be tranferred from hbq to dlq
								TransferFlag 	= istNachrichtVerschiebenMoeglich(HBQ2, DLQ2),
								if
									TransferFlag == true 	-> 	{NewerHBQ, NewDLQ} = verschiebeVonHBQInDLQ(HBQ2, DLQ2, DLQLimit, Servername);
									true 					-> 	NewDLQ 		= DLQ2,
																NewerHBQ 	= HBQ2
								end,
								
								% if limit is over the dlq must get an error message
								HBQLimitUeberschrittenFlag  = istHBQLimitUeberschritten(NewerHBQ, DLQLimit),
								if
									HBQLimitUeberschrittenFlag == true 	->  NewerDLQ = fehlerNachrichtInDLQEinfuegen(NewerHBQ, NewDLQ, Servername);
									true								->  NewerDLQ = NewDLQ
								end,
								{NewerHBQ, NewerDLQ}.
	
	
%logfile erstellen
writeToLog(MyLogFileName, Text) ->  werkzeug:logging(lists:concat([MyLogFileName,".log"]), lists:concat([Text,"\r\n"])).	
			
% OPERATIONS FOR BOTH --------------------------------------		

% appens the elem tuple to the end of a list - tested!
%(aa@Uhura-PC)51> C = queuemanager:fuegeHintenAn({'vierte',4}, A).
%[{erste,1},{zweite,2},{dritte,3},{vierte,4}]
fuegeHintenAn({Nachricht, Number}, Queue) -> lists:append(Queue, [{Nachricht, Number}]).			
			

% init the search for highest number - tested! 
gebeMaxNr(Queue)	-> gebeMaxNrR(Queue, 0).		
gebeMaxNrR([], Nr) 										-> 	Nr;
gebeMaxNrR([{_HeadMessage, HeadNumber}|QueueTail], Nr)	-> 	gebeMaxNrR(QueueTail,
															erlang:max(HeadNumber,Nr)).
	
	
% init the search for lowest number - for more comfort the needed additional param is set by head method - tested! 
gebeMinNr(Queue)	-> 	StartNumber = gebeMaxNr(Queue),
						gebeMinNrR(Queue, StartNumber).		
gebeMinNrR([], Nr) 										-> 	Nr;
gebeMinNrR([{_HeadMessage, HeadNumber}|QueueTail], Nr)	-> 	Min = erlang:min(HeadNumber,Nr),
															gebeMinNrR(QueueTail, Min).	

												
% is there a message to put from HBQ to DLQ?
istNachrichtVerschiebenMoeglich(HBQ, DLQ)	-> 	MinFromHBQ = gebeMinNr(HBQ),
												MaxFromDLQ = gebeMaxNr(DLQ),								
												if	
													DLQ == [] 														-> true;
													MaxFromDLQ < MinFromHBQ andalso (MinFromHBQ - MaxFromDLQ) == 1 	-> true;
													%MaxFromDLQ < MinFromHBQ andalso (MinFromHBQ - MaxFromDLQ) > 1 	-> false;
													true -> false
												end.


										
% put messages  from HBQ to DLQ
verschiebeVonHBQInDLQ(HBQ, DLQ, DLQLimit, Servername) ->	
												MinFromHBQ 		= gebeMinNr(HBQ),
												{Nachricht, Number} 	= gebeNachrichtMitNr(MinFromHBQ, HBQ),
												
												NewHBQ 			= lists:delete({Nachricht, Number},HBQ),
												NewNachricht 		= lists:concat([Nachricht, " DLQ: ", werkzeug:timeMilliSecond()]),
												writeToLog(Servername, NewNachricht),
												NewDLQ 				= fuegeHintenAn({NewNachricht, Number}, DLQ),
																										
												% if queue is over limit delete first elem
												DLQLimitUeberschritten = istDLQLimitUeberschritten(NewDLQ, DLQLimit),
												if
													DLQLimitUeberschritten == true 	-> 	MinElem 	= gebeMinElemFromDLQ(NewDLQ),
																						NewerDLQ 	= lists:delete(MinElem, NewDLQ);
													true 							-> 	NewerDLQ = 	NewDLQ	
												end,
													
												%are there more message to put from hbq to dlq
												TransferFlag 	= istNachrichtVerschiebenMoeglich(NewHBQ, NewerDLQ),
												if	
													TransferFlag == true -> verschiebeVonHBQInDLQ(NewHBQ, NewerDLQ, DLQLimit, Servername);
													true 				 -> {NewHBQ, NewerDLQ}
												end.

fehlerNachrichtInDLQEinfuegen(HBQ, DLQ, Servername) -> 	MinFromHBQ  = gebeMinNr(HBQ),
							MaxFromDLQ = gebeMaxNr(DLQ),
							MessageString = lists:concat(["***Fehlernachricht fuer Nachrichtennummern von ", MaxFromDLQ+1, " bis " , MinFromHBQ-1, " am ", werkzeug:timeMilliSecond()]),
							writeToLog(Servername, MessageString),
							NewDLQ = fuegeHintenAn({MessageString, MinFromHBQ-1}, DLQ),
							NewDLQ.
											
												
										
% OPERATIONS FOR DLQ --------------------------------------
% Testdatastructure : [{'erste',1},{'zweite',2},{'dritte',3}]


% getter for message with equal or next higher number - tested!
gebeNaechsteNachricht(NachrichtenNummer,DLQ) -> gebeNaechsteNachrichtR(NachrichtenNummer,DLQ).
%gebeNaechsteNachrichtR(NachrichtenNummer, []) -> 		% !!! was wenn dlq leer!
gebeNaechsteNachrichtR(NachrichtenNummer, [{HeadMessage,HeadNumber}| DLQTail]) ->
														if
															NachrichtenNummer == HeadNumber -> {HeadMessage,HeadNumber};
															HeadNumber > NachrichtenNummer -> {HeadMessage,HeadNumber};
															true -> gebeNaechsteNachrichtR(NachrichtenNummer, DLQTail)
														end.
						
						
% get lowest number fromDLQ - must be found at head - tested!
gebeMinNrFromDLQ(([{_HeadMessage, HeadNumber}|_DLQTail])) -> HeadNumber.


% get lowest elem fromDLQ - must be found at head
gebeMinElemFromDLQ([{HeadMessage, HeadNumber}|_DLQTail]) -> {HeadMessage, HeadNumber}.


istDLQLimitUeberschritten(DLQ, DLQLimit) -> QueueLength = erlang:length(DLQ),
											if
												QueueLength > DLQLimit -> true;
												true -> false
											end.
	
% OPERATIONS FOR HBQ --------------------------------------	

fuegeNachrichtInHBQ({Nachricht, Number}, HBQ, Servername) -> 	NewNachricht = lists:concat([Nachricht, " HBQ: ", werkzeug:timeMilliSecond()]),
																writeToLog(Servername, NewNachricht),
																NewHBQ = fuegeHintenAn({NewNachricht, Number}, HBQ),
																{NewHBQ}.
										
										
gebeNachrichtMitNr(NachrichteNummer, HBQ) -> gebeNachrichtMitNrR(NachrichteNummer, HBQ).
		
gebeNachrichtMitNrR(_NachrichteNummer, []) 									-> {'keine Nachricht',0};
gebeNachrichtMitNrR(NachrichteNummer, [{HeadMessage, HeadNumber}|HBQTail])	-> 
									if
										NachrichteNummer =< HeadNumber -> {HeadMessage, HeadNumber};
										true -> gebeNachrichtMitNrR(NachrichteNummer, HBQTail)
									end.


istHBQLimitUeberschritten(HBQ, DLQLimit) -> QueueLength = erlang:length(HBQ),
											HBQLimit 	= erlang:round(DLQLimit/2),
											if
												QueueLength > HBQLimit -> true;
												true -> false
											end.
											
											
% if a message could never be pushed to dlq because of message index it is deleted here
loescheVeralteteUngueltigeNachrichtenAusHBQ(HBQ, DLQ) -> 	MaxFromDLQ = gebeMaxNr(DLQ),						
															MinFromHBQ = gebeMinNr(HBQ),
															if
																 MinFromHBQ > MaxFromDLQ ->  {HBQ, DLQ}; % everything is fine - stop the call
																
																% delete also funny tries to corrupt our queue with messages with wrong and crazy numbers
																% pessimistic deleting of messages - all not matching on upper condition is wrong!
																true -> UngueltigeNachricht = gebeNachrichtMitNr(MinFromHBQ, HBQ),
																% LOGFILE 
																		NewHBQ 				= lists:delete(UngueltigeNachricht,HBQ),
																		loescheVeralteteUngueltigeNachrichtenAusHBQ(NewHBQ, DLQ)
															end.
																


			
			
			
			